ioemu: SDL rendering using OpenGL
authorKeir Fraser <keir.fraser@citrix.com>
Tue, 18 Mar 2008 11:47:58 +0000 (11:47 +0000)
committerKeir Fraser <keir.fraser@citrix.com>
Tue, 18 Mar 2008 11:47:58 +0000 (11:47 +0000)
Add opengl support for rendering the guest framebuffer in the SDL
window. SDL is needed anyway to open the window and handle the
events. Opengl rendering is optional and can be turned off at both
compile time and in the vm configuration file.

Some of the benefits of using opengl are:

 - faster rendering, less CPU intensive, especially with good graphic
   cards;
 - makes the window resizing possible and hardware accelerated, thus
   very efficient and smooth;
 - allows other optimizations like sharing directly a buffer in vram
   with the guest (not yet implemented).

Signed-off-by: Stefano Stabellini <stefano.stabellini@eu.citrix.com>
tools/examples/xmexample.hvm
tools/ioemu/configure
tools/ioemu/sdl.c
tools/ioemu/vl.c
tools/ioemu/vl.h
tools/python/xen/xend/XendConfig.py
tools/python/xen/xend/image.py
tools/python/xen/xm/create.py

index a8153b279ae812bac66ba308821b7c2b874d765b..1a1f2487a680e43f55f0399f51b929ed94b1219c 100644 (file)
@@ -134,6 +134,11 @@ device_model = '/usr/' + arch_libdir + '/xen/bin/qemu-dm'
 # enable SDL library for graphics, default = 0
 sdl=0
 
+#----------------------------------------------------------------------------
+# enable OpenGL for texture rendering inside the SDL window, default = 1
+# valid only if sdl is enabled.
+opengl=1
+
 #----------------------------------------------------------------------------
 # enable VNC library for graphics, default = 1
 vnc=1
index 3e3b427b8717f6988e020ac7a70a4484834743ce..b0a4fb857c42dafd6982e46f013f2be1f8e22813 100755 (executable)
@@ -190,6 +190,8 @@ for opt do
   ;;
   --disable-sdl) sdl="no"
   ;;
+  --disable-opengl) opengl="no"
+  ;;
   --enable-coreaudio) coreaudio="yes"
   ;;
   --enable-alsa) alsa="yes"
@@ -538,6 +540,26 @@ else
  fi
 fi # -z $sdl
 
+##########################################
+# OpenGL test
+
+if test -z "$opengl" && test "$sdl" = "yes"
+then
+cat > $TMPC << EOF
+#include <GL/gl.h>
+#ifndef GL_TEXTURE_RECTANGLE_ARB
+#error "Opengl doesn't support GL_TEXTURE_RECTANGLE_ARB"
+#endif
+int main( void ) { return (int) glGetString(GL_EXTENSIONS); }
+EOF
+if $cc -o $TMPE $TMPC -lGL 2> /dev/null
+then
+opengl="yes"
+else
+opengl="no"
+fi
+fi
+
 ##########################################
 # alsa sound support libraries
 
@@ -612,6 +634,7 @@ echo "SDL support       $sdl"
 if test "$sdl" != "no" ; then
     echo "SDL static link   $sdl_static"
 fi
+echo "OpenGL support    $opengl"
 echo "mingw32 support   $mingw32"
 echo "Adlib support     $adlib"
 echo "CoreAudio support $coreaudio"
@@ -995,6 +1018,15 @@ if test "$target_user_only" = "no"; then
     fi
 fi
 
+if test $opengl = "yes"
+then
+    echo "#define CONFIG_OPENGL 1" >> $config_h
+    echo "CONFIG_OPENGL=yes" >> $config_mak
+    echo "SDL_CFLAGS+=-I/usr/include/GL" >> $config_mak
+    echo "SDL_LIBS+=-lXext" >> $config_mak
+    echo "SDL_LIBS+=-lGL" >> $config_mak
+fi
+
 if test "$cocoa" = "yes" ; then
     echo "#define CONFIG_COCOA 1" >> $config_h
     echo "CONFIG_COCOA=yes" >> $config_mak
index f9e140d42e4da2d8d3e59ed0705681a5d40f0565..20ce97bfa4d0cb618ed735e74883deaab171632e 100644 (file)
 #include <signal.h>
 #endif
 
+#ifdef CONFIG_OPENGL
+#include <SDL_opengl.h>
+#endif
+
 static SDL_Surface *screen;
 static SDL_Surface *shared = NULL;
 static int gui_grab; /* if true, all keyboard/mouse events are grabbed */
@@ -44,6 +48,99 @@ static int width, height;
 static SDL_Cursor *sdl_cursor_normal;
 static SDL_Cursor *sdl_cursor_hidden;
 static int absolute_enabled = 0;
+static int opengl_enabled;
+
+#ifdef CONFIG_OPENGL
+static GLint tex_format;
+static GLint tex_type;
+static GLuint texture_ref = 0;
+static GLint gl_format;
+
+static void opengl_setdata(DisplayState *ds, void *pixels)
+{
+    glEnable(GL_TEXTURE_RECTANGLE_ARB);
+    glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
+    glClearColor(0, 0, 0, 0);
+    glDisable(GL_BLEND);
+    glDisable(GL_LIGHTING);
+    glDisable(GL_DEPTH_TEST);
+    glDepthMask(GL_FALSE);
+    glDisable(GL_CULL_FACE);
+    glViewport( 0, 0, screen->w, screen->h);
+    glMatrixMode(GL_PROJECTION);
+    glLoadIdentity();
+    glOrtho(0, screen->w, screen->h, 0, -1,1);
+    glMatrixMode(GL_MODELVIEW);
+    glLoadIdentity();
+    glClear(GL_COLOR_BUFFER_BIT);
+    ds->data = pixels;
+
+    if (texture_ref) {
+        glDeleteTextures(1, &texture_ref);
+        texture_ref = 0;
+    }
+
+    glGenTextures(1, &texture_ref);
+    glBindTexture(GL_TEXTURE_RECTANGLE_ARB, texture_ref);
+    glPixelStorei(GL_UNPACK_LSB_FIRST, 1);
+    switch (ds->depth) {
+        case 8:
+            tex_format = GL_RGB;
+            tex_type = GL_UNSIGNED_BYTE_3_3_2;
+            glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
+            break;
+        case 16:
+            tex_format = GL_RGB;
+            tex_type = GL_UNSIGNED_SHORT_5_6_5;
+            glPixelStorei (GL_UNPACK_ALIGNMENT, 2);
+            break;
+        case 24:
+            tex_format = GL_BGR;
+            tex_type = GL_UNSIGNED_BYTE;
+            glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
+            break;
+        case 32:
+            if (!ds->bgr) {
+                tex_format = GL_BGRA;
+                tex_type = GL_UNSIGNED_BYTE;
+            } else {
+                tex_format = GL_RGBA;
+                tex_type = GL_UNSIGNED_BYTE;                
+            }
+            glPixelStorei (GL_UNPACK_ALIGNMENT, 4);
+            break;
+    }   
+    glPixelStorei(GL_UNPACK_ROW_LENGTH, (ds->linesize * 8 / ds->depth) - ds->width);
+    glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, gl_format, ds->width, ds->height, 0, tex_format, tex_type, pixels);
+    glTexParameterf(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_PRIORITY, 1.0);
+    glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+    glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+    glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+    glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+    glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0);
+}
+
+static void opengl_update(DisplayState *ds, int x, int y, int w, int h)
+{  
+    int bpp = ds->depth / 8;
+    GLvoid *pixels = ds->data + y * ds->linesize + x * bpp;
+    glBindTexture(GL_TEXTURE_RECTANGLE_ARB, texture_ref);
+    glPixelStorei(GL_UNPACK_ROW_LENGTH, (ds->linesize / bpp) - w);
+    glTexSubImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, x, y, w, h, tex_format, tex_type, pixels);
+    glBegin(GL_QUADS);
+        glTexCoord2d(0, 0);
+        glVertex2d(0, 0);
+        glTexCoord2d(ds->width, 0);
+        glVertex2d(screen->w, 0);
+        glTexCoord2d(ds->width, ds->height);
+        glVertex2d(screen->w, screen->h);
+        glTexCoord2d(0, ds->height);
+        glVertex2d(0, screen->h);
+    glEnd();
+    glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0);
+    SDL_GL_SwapBuffers();
+}
+#endif
 
 static void sdl_update(DisplayState *ds, int x, int y, int w, int h)
 {
@@ -96,17 +193,26 @@ static void sdl_resize(DisplayState *ds, int w, int h, int linesize)
 
     //    printf("resizing to %d %d\n", w, h);
 
-    flags = SDL_HWSURFACE|SDL_ASYNCBLIT|SDL_HWACCEL|SDL_DOUBLEBUF|SDL_HWPALETTE;
-    if (gui_fullscreen)
-        flags |= SDL_FULLSCREEN;
+#ifdef CONFIG_OPENGL
+    if (ds->shared_buf && opengl_enabled)
+        flags = SDL_OPENGL|SDL_RESIZABLE;
+    else
+#endif
+        flags = SDL_HWSURFACE|SDL_ASYNCBLIT|SDL_HWACCEL|SDL_DOUBLEBUF|SDL_HWPALETTE;
 
+    if (gui_fullscreen) {
+        flags |= SDL_FULLSCREEN;
+        flags &= ~SDL_RESIZABLE;
+    }
+    
     width = w;
     height = h;
 
  again:
     screen = SDL_SetVideoMode(w, h, 0, flags);
+#ifndef CONFIG_OPENGL
     if (!screen) {
-        fprintf(stderr, "Could not open SDL display\n");
+        fprintf(stderr, "Could not open SDL display: %s\n", SDL_GetError());
         exit(1);
     }
     if (!screen->pixels && (flags & SDL_HWSURFACE) && (flags & SDL_FULLSCREEN)) {
@@ -115,9 +221,10 @@ static void sdl_resize(DisplayState *ds, int w, int h, int linesize)
     }
 
     if (!screen->pixels) {
-        fprintf(stderr, "Could not open SDL display\n");
+        fprintf(stderr, "Could not open SDL display: %s\n", SDL_GetError());
         exit(1);
     }
+#endif
     ds->width = w;
     ds->height = h;
     if (!ds->shared_buf) {
@@ -131,6 +238,25 @@ static void sdl_resize(DisplayState *ds, int w, int h, int linesize)
         ds->linesize = screen->pitch;
     } else {
         ds->linesize = linesize;
+#ifdef CONFIG_OPENGL
+        switch(screen->format->BitsPerPixel) {
+        case 8:
+            gl_format = GL_RGB;
+            break;
+        case 16:
+            gl_format = GL_RGB;
+            break;
+        case 24:
+            gl_format = GL_RGB;
+            break;
+        case 32:
+            if (!screen->format->Rshift)
+                gl_format = GL_BGRA;
+            else
+                gl_format = GL_RGBA;
+            break;
+        };
+#endif
     }
 }
 
@@ -139,7 +265,13 @@ static void sdl_colourdepth(DisplayState *ds, int depth)
     if (!depth || !ds->depth) return;
     ds->shared_buf = 1;
     ds->depth = depth;
-    ds->linesize = width * depth / 8; 
+    ds->linesize = width * depth / 8;
+#ifdef CONFIG_OPENGL
+    if (opengl_enabled) {
+        ds->dpy_update = opengl_update;
+        ds->dpy_setdata = opengl_setdata;
+    }
+#endif
 }
 
 /* generic keyboard conversion */
@@ -331,8 +463,8 @@ static void sdl_send_mouse_event(int dx, int dy, int dz, int state)
        }
 
        SDL_GetMouseState(&dx, &dy);
-        dx = dx * 0x7FFF / (width - 1);
-        dy = dy * 0x7FFF / (height - 1);
+        dx = dx * 0x7FFF / (screen->w - 1);
+        dy = dy * 0x7FFF / (screen->h - 1);
     } else if (absolute_enabled) {
        sdl_show_cursor();
        absolute_enabled = 0;
@@ -371,7 +503,7 @@ static void sdl_refresh(DisplayState *ds)
     while (SDL_PollEvent(ev)) {
         switch (ev->type) {
         case SDL_VIDEOEXPOSE:
-            sdl_update(ds, 0, 0, screen->w, screen->h);
+            sdl_update(ds, 0, 0, ds->width, ds->height);
             break;
         case SDL_KEYDOWN:
         case SDL_KEYUP:
@@ -528,6 +660,16 @@ static void sdl_refresh(DisplayState *ds)
                }
            }
             break;
+#ifdef CONFIG_OPENGL
+        case SDL_VIDEORESIZE:
+        {
+            SDL_ResizeEvent *rev = &ev->resize;
+            screen = SDL_SetVideoMode(rev->w, rev->h, 0, SDL_OPENGL|SDL_RESIZABLE);
+            opengl_setdata(ds, ds->data);
+            opengl_update(ds, 0, 0, ds->width, ds->height);
+            break;
+        }
+#endif
         default:
             break;
         }
@@ -536,13 +678,17 @@ static void sdl_refresh(DisplayState *ds)
 
 static void sdl_cleanup(void) 
 {
+#ifdef CONFIG_OPENGL
+    if (texture_ref) glDeleteTextures(1, &texture_ref);
+#endif
     SDL_Quit();
 }
 
-void sdl_display_init(DisplayState *ds, int full_screen)
+void sdl_display_init(DisplayState *ds, int full_screen, int opengl)
 {
     int flags;
     uint8_t data = 0;
+    opengl_enabled = opengl;
 
 #if defined(__APPLE__)
     /* always use generic keymaps */
index 9e3699705f630943ffba1c5991537d41e40d7b3d..7a717f53bff270000bcafc9c31d89bcc0bd7efc2 100644 (file)
@@ -174,6 +174,11 @@ int graphic_height = 600;
 #endif
 int graphic_depth = 15;
 int full_screen = 0;
+#ifdef CONFIG_OPENGL
+int opengl_enabled = 1;
+#else
+int opengl_enabled = 0;
+#endif
 int no_quit = 0;
 CharDriverState *serial_hds[MAX_SERIAL_PORTS];
 CharDriverState *parallel_hds[MAX_PARALLEL_PORTS];
@@ -6488,6 +6493,9 @@ void help(void)
 #ifdef CONFIG_SDL
            "-no-quit        disable SDL window close capability\n"
 #endif
+#ifdef CONFIG_OPENGL
+           "-disable-opengl disable OpenGL rendering, using SDL"
+#endif
 #ifdef TARGET_I386
            "-no-fd-bootchk  disable boot signature checking for floppy disks\n"
 #endif
@@ -6666,6 +6674,7 @@ enum {
     QEMU_OPTION_loadvm,
     QEMU_OPTION_full_screen,
     QEMU_OPTION_no_quit,
+    QEMU_OPTION_disable_opengl,
     QEMU_OPTION_pidfile,
     QEMU_OPTION_no_kqemu,
     QEMU_OPTION_kernel_kqemu,
@@ -6763,6 +6772,7 @@ const QEMUOption qemu_options[] = {
 #ifdef CONFIG_SDL
     { "no-quit", 0, QEMU_OPTION_no_quit },
 #endif
+    { "disable-opengl", 0, QEMU_OPTION_disable_opengl },
     { "pidfile", HAS_ARG, QEMU_OPTION_pidfile },
     { "win2k-hack", 0, QEMU_OPTION_win2k_hack },
     { "usbdevice", HAS_ARG, QEMU_OPTION_usbdevice },
@@ -7534,6 +7544,9 @@ int main(int argc, char **argv)
                 no_quit = 1;
                 break;
 #endif
+            case QEMU_OPTION_disable_opengl:
+                opengl_enabled = 0;
+                break;
             case QEMU_OPTION_pidfile:
                 create_pidfile(optarg);
                 break;
@@ -7860,7 +7873,7 @@ int main(int argc, char **argv)
        xenstore_write_vncport(vnc_display_port);
     } else {
 #if defined(CONFIG_SDL)
-        sdl_display_init(ds, full_screen);
+        sdl_display_init(ds, full_screen, opengl_enabled);
 #elif defined(CONFIG_COCOA)
         cocoa_display_init(ds, full_screen);
 #else
index 3f7ff0422f43abe95245ae01191772c2c688870c..0a90fcc29e74d06cfd50de8003a3d2fb723618c4 100644 (file)
@@ -982,7 +982,7 @@ void isa_cirrus_vga_init(DisplayState *ds, uint8_t *vga_ram_base,
                          unsigned long vga_ram_offset, int vga_ram_size);
 
 /* sdl.c */
-void sdl_display_init(DisplayState *ds, int full_screen);
+void sdl_display_init(DisplayState *ds, int full_screen, int opengl_enable);
 
 /* cocoa.m */
 void cocoa_display_init(DisplayState *ds, int full_screen);
index ddc2ef8d9a356543c81ee305fd23bdfccc0fe9f5..eaa9eee01e707fb81e290dfd2910f5fffd11bfd4 100644 (file)
@@ -142,6 +142,7 @@ XENAPI_PLATFORM_CFG_TYPES = {
     'rtc_timeoffset': int,
     'serial': str,
     'sdl': int,
+    'opengl': int,
     'soundhw': str,
     'stdvga': int,
     'usb': int,
index 9c09e1b8d648e8ab157167137a6e75ff1a756aac..0a5b2efdf4fa6b7640b2d6c6c0adf1bf0c4b1fc4 100644 (file)
@@ -263,7 +263,8 @@ class ImageHandler:
 
         elif has_sdl:
             # SDL is default in QEMU.
-            pass
+            if int(vmConfig['platform'].get('opengl', 1)) != 1 :
+                ret.append('-disable-opengl')
         else:
             ret.append('-nographic')
 
index 7a3d764c6bafac84d0b3e34a769cb2bfb075a35f..745f0efc59e6d8c7ecd094bf1c312c54e817d057 100644 (file)
@@ -504,6 +504,10 @@ gopts.var('sdl', val='',
           fn=set_value, default=None,
           use="""Should the device model use SDL?""")
 
+gopts.var('opengl', val='',
+          fn=set_value, default=None,
+          use="""Enable\Disable OpenGL""")
+
 gopts.var('display', val='DISPLAY',
           fn=set_value, default=None,
           use="X11 display to use")
@@ -745,7 +749,7 @@ def configure_hvm(config_image, vals):
              'vnc', 'vncdisplay', 'vncunused', 'vncconsole', 'vnclisten',
              'sdl', 'display', 'xauthority', 'rtc_timeoffset', 'monitor',
              'acpi', 'apic', 'usb', 'usbdevice', 'keymap', 'pci', 'hpet',
-             'guest_os_type', 'hap']
+             'guest_os_type', 'hap', 'opengl']
 
     for a in args:
         if a in vals.__dict__ and vals.__dict__[a] is not None: